﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TSOL.FastSPWeb.ValueObjects;
using System.Threading;
using Microsoft.SharePoint;

namespace TSOL.FastSPWeb.Core
{
    internal partial class FastSPWebThreadWorkerQueue : IDisposable
    {
        internal long UseOfOtherVersion = 0;
        internal long WaitingForThread = 0;
        internal long FastSPItemsWaitingForFreeCacheEntity = 0;
        internal long FastSPListsWaitingForFreeCacheEntity = 0;
        internal int MaxCopiesOfElevatedWeb = 10;
        internal int MaxCopiesOfElevatedItems = 5;
        internal int MaxCopiesOfUserWeb = 1;
        internal int MaxNumberOfThreads = 255;
        internal int StartedThreads = 0;
        internal TimeSpan DefaultItemsCachedTimeSpan = new TimeSpan(0, 20, 0);
        object locker = new object();
        internal Thread[] workers;
        internal Queue<FastSPWebQueueClass> taskQ = new Queue<FastSPWebQueueClass>();

        internal static void Create(int threadCount)
        {
            GlobalSPWebThreadWorkerQueue = new FastSPWebThreadWorkerQueue(threadCount);
            FastSPWebFactoryThread.Start();
            FastSPItemsFactoryThread.Start();
            FastSPItemsGarbageCollectorThread.Start();
            FastSPWebGarbageCollectorThread.Start();
        }

        internal void StartThreads(int threadCount)
        {
            int startedThreads = workers.Count(a => a.ThreadState != ThreadState.Unstarted);

            int threadsToStart = threadCount - startedThreads;

            foreach (Thread unstartedThread in workers)
            {
                if (threadsToStart < 1)
                    break;

                if (unstartedThread.ThreadState == ThreadState.Unstarted)
                {
                    unstartedThread.Start();
                    threadsToStart--;
                }
            }
            StartedThreads = FastSPWebThreadWorkerQueue.GlobalSPWebThreadWorkerQueue.workers.Count(a => a.ThreadState != ThreadState.Unstarted);
        }

        FastSPWebThreadWorkerQueue(int threadCount)
        {
            if (threadCount > MaxNumberOfThreads) threadCount = MaxNumberOfThreads;
            if (threadCount < 1) threadCount = 1;

            workers = new Thread[MaxNumberOfThreads];
            for (int i = 0; i < MaxNumberOfThreads; i++)
                workers[i] = new Thread(ConsumeSPWebTasks);

            for (int i = 0; i < threadCount; i++)
                workers[i].Start();

            StartedThreads = threadCount;
        }

        public void Dispose()
        {
            foreach (Thread worker in workers)
            {
                if (worker.ThreadState != ThreadState.Unstarted) 
                    QueueOpenWeb(null, null, null);
            }
        }

        void ConsumeSPWebTasks()
        {
            while (true)
            {
                FastSPWebQueueClass task;
                lock (locker)
                {
                    while (taskQ.Count == 0) Monitor.Wait(locker);
                    task = taskQ.Dequeue();
                }
                if (task.Proc == null) return;         // Application is disposing

                SPWebCacheEntity _cache = null;
                var hashKey = task.UserToken == null ?
                      (task.SiteGuid.ToString() + task.WebGuid.ToString() + true.ToString() + "elevated".GetHashCode().ToString()).GetHashCode()
                    : (task.SiteGuid.ToString() + task.WebGuid.ToString() + true.ToString() + task.UserUniqueID.GetHashCode().ToString()).GetHashCode();

                SPWebCacheCollection _SPCacheCollection = null;
                while (_cache == null)
                {
                    while (!WebsCacheDictionary.TryGetValue(hashKey, out _SPCacheCollection)) FastSPWebFactoryThread.StartImmediate(hashKey, task);

                    if (_SPCacheCollection.Count == 0) FastSPWebFactoryThread.StartImmediate(hashKey, task);
                    _cache = _SPCacheCollection.FirstOrDefault(a => a.Free == true);
                    if (_cache != default(SPWebCacheEntity))
                        break;
                    else
                    {
                        lock (_SPCacheCollection) _SPCacheCollection.WaitingForFreeWeb++;
                        WaitingForThread++;
                    }
                }
                lock (_cache)
                {
                    _cache.Free = false;
                    task.Proc(_cache.Site, _cache.Web, _cache.GetHashCode().ToString());
                    _cache.LastUsed = DateTime.Now;
                    _cache.Free = true;
                }

            }
        }
    }
}
